package mb.commons;

import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * Udostępnia funkcje do odczytu instrukcji SQL z pliku XML.
 *
 * @author Michał Budziło
 *
 */
public class XmlHelper {
	
	/**
	 * Pole udostępnia funkcje do pobierania informacji zapisanych w pliku XML.
	 */
	private XPath xpath;
	
	/**
	 * Reprezentuje źródłowy dokument XML
	 */
	private InputSource inputSource;
	
	/**
	 * Ścieżka rzeczywista do pliku XML.
	 */
	private String realPath;
	
	/**
	 * Konstruktor ustawiający ścieżkę do pliku XML.
	 * @param path ścieżka względna do pliku XML.
	 */
	public XmlHelper(String path) {
		realPath = ((ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext()).getRealPath(path);
		xpath = XPathFactory.newInstance().newXPath();
		inputSource = new InputSource(realPath);
	}
	
	/**
	 * Metoda zwraca instrukcję  SQL zapisaną w strukturze dokumentu XML.
	 * @param tableName nazwa wyrażenia do odczytu, zawarta w bloku name dokumentu XML.
	 * @param expression ścieżka dostępu do wyrażenia w strukturze dokumentu XML.
	 * @return instrukcja SQL.
	 * @throws XPathExpressionException jeśli odczyt z pliku XML nie powiedzie się.
	 */
	public String getSqlString(String tableName, String expression) throws XPathExpressionException{		
		NodeList nodes = (NodeList)xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
		
		for(int i = 1; i <= nodes.getLength(); i++){
			String name = xpath.evaluate(expression + "["+ i +"]/name", inputSource);
			if(expression.contains("prepareSelection/table")){
				if(name.compareTo(tableName) == 0){
					expression = expression.concat("["+ i +"]/conditions");
				}
			} else if(expression.contains("prepareMain") || 
					expression.contains("selectMain") || 
					expression.contains("selectExtra") || 
					expression.contains("clean")){
				if(	name.compareTo(tableName) == 0){
					expression = expression.concat("["+ i +"]/body");
				}
			}
		}
		return (xpath.evaluate(expression, inputSource));
	}
	
	/**
	 * Metoda zwraca instrukcję SQL zapisaną w strukturze dokumentu XML, w gałęzi extraCondition. 
	 * @param tableName nazwa wyrażenia do odczytu, zawarta w bloku name dokumentu XML.
	 * @param conditionName nazwa dodatkowego warunku.
	 * @param expression ścieżka dostępu do wyrażenia w strukturze dokumentu XML.
	 * @return instrukcja SQL
	 * @throws XPathExpressionException jeśli odczyt z pliku XML nie powiedzie się.
	 */
	public String getExtraCondition(String tableName, String conditionName, String expression) throws XPathExpressionException{
		String retString = "";
		NodeList nodes = (NodeList)xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
		NodeList coditionNodes = (NodeList)xpath.evaluate(expression.concat("/extraCondition"), inputSource, XPathConstants.NODESET);
		for(int i = 1; i <= nodes.getLength(); i++){
			String name = xpath.evaluate(expression + "["+ i +"]/name", inputSource);
			if(expression.contains("prepareSelection/table")){
				if(name.compareTo(tableName) == 0){
					expression = expression.concat("["+ i +"]/conditions");
				}
			} else if(expression.contains("prepareMain") || 
					expression.contains("selectMain") || 
					expression.contains("selectExtra") || 
					expression.contains("clean")){
				if(	name.compareTo(tableName) == 0){
					for(int k = 1; k <= coditionNodes.getLength(); k++){
						String tmpConditionName = xpath.evaluate(expression.concat("["+i+"]/extraCondition") + "["+ k +"]/name", inputSource);
						if(	tmpConditionName.compareTo(conditionName) == 0){
							retString = xpath.evaluate(expression.concat("["+i+"]/extraCondition" + "["+ k +"]/body"), inputSource);
							break;
						}
					}
					retString = retString.concat(xpath.evaluate(expression.concat("["+ i +"]/group"), inputSource));
					retString = retString.concat(xpath.evaluate(expression.concat("["+ i +"]/order"), inputSource));
					retString = retString.concat(xpath.evaluate(expression.concat("["+ i +"]/temp"), inputSource));	
				}
			}
		}
		return retString;
	}
	
	/**
	 * Metoda zwraca instrukcję SQL zapisaną w strukturze dokumentu XML, w bloku select dokumentu XML.
	 * Używana jeśli wynik zapytania SQL zależy od warunków  użytych w klauzuli SELECT. 
	 * @param tableName nazwa wyrażenia do odczutu, zawarta w bloku name dokumentu XML.
	 * @param conditionName nazwa warunku z klauzuli SELECT.
	 * @param expression ścieżka dostępu do wyrażenia w strukturze dokumentu XML.
	 * @return instrukcja SQL
	 * @throws XPathExpressionException jeśli odczyt z pliku XML nie powiedzie się.
	 */
	public String getSelect(String tableName, String conditionName, String expression) throws XPathExpressionException{
		String retString = "";
		NodeList nodes = (NodeList)xpath.evaluate(expression, inputSource, XPathConstants.NODESET);
		NodeList coditionNodes = (NodeList)xpath.evaluate(expression.concat("/select"), inputSource, XPathConstants.NODESET);
		for(int i = 1; i <= nodes.getLength(); i++){
			String name = xpath.evaluate(expression + "["+ i +"]/name", inputSource);
			if(expression.contains("prepareSelection/table")){
				if(name.compareTo(tableName) == 0){
					expression = expression.concat("["+ i +"]/conditions");
				}
			} else if(expression.contains("prepareMain") || 
					expression.contains("selectMain") || 
					expression.contains("selectExtra") || 
					expression.contains("clean")){
				if(	name.compareTo(tableName) == 0){
					for(int k = 1; k <= coditionNodes.getLength(); k++){
						String tmpConditionName = xpath.evaluate(expression.concat("/select") + "["+ k +"]/name", inputSource);
						if(	tmpConditionName.compareTo(conditionName) == 0){
							retString = xpath.evaluate(expression.concat("/select" + "["+ k +"]/body"), inputSource);
							break;
						}
					}
				}
			}
		}
		return retString;
	}
}
